<template>
  <el-dialog title="选择图片" :visible.sync="show" class="select-file-List-dialog" :close-on-click-modal="false" :show-close="!disableAction" width="1170px" top="50px" append-to-body @closed="closed">
    <div slot="title">
      选择图片
      <el-popover placement="right" trigger="hover">
        <i slot="reference" class="el-icon-question pointer" style="color: #606266" />
        <div>
          <div>1. 双击 <svg-icon icon-class="folder" /> 打开文件夹</div>
          <div>2. 在 <svg-icon icon-class="folder" /> 右键可进行更多操作</div>
        </div>
      </el-popover>
    </div>

    <div class="loading" v-loading="loadingText" :element-loading-text="loadingText" element-loading-background="rgba(255, 255, 255, 0.5)" v-if="loadingText" />

    <el-form size="mini" inline @submit.native.prevent :class="{ disabled: disableAction }">
      <el-form-item label="选择检查项">
        <el-select v-model="chkitemCode" placeholder="请选择检查项" :loading="firChkLoading" style="width: 200px" @change="onChangeChkCode">
          <el-option v-for="item in firChkList" :key="item.chkitemCode" :label="item.chkitemName" :value="item.chkitemCode" />
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-upload" @click="clickUpload">上传图片</el-button>
        <el-button type="success" icon="el-icon-circle-plus" :loading="addFolderLoading" @click="addFolder">新建文件夹</el-button>
      </el-form-item>
    </el-form>

    <el-breadcrumb class="folder-paths" separator="/">
      <el-breadcrumb-item v-if="!chkitemCode">
        <a @click="switchFolder({ action: 'root' })">根目录</a>
      </el-breadcrumb-item>
      <el-breadcrumb-item v-if="showChkName">
        <a @click="switchFolder({ action: 'chkCode' })">{{ showChkName }}</a>
      </el-breadcrumb-item>
      <el-breadcrumb-item v-for="(item, index) in showFolderPaths" :key="index">
        <span v-if="item.taskFileId === undefined">{{ item.showFileName }}</span>
        <a @click="switchFolder(item, index)" :title="item.fileName" v-else>{{ item.showFileName }}</a>
      </el-breadcrumb-item>
    </el-breadcrumb>

    <div class="upload-box">
      <el-upload
        ref="upload"
        :action="FILE_MULTI_UPLOAD_URL"
        :data="{ type: 'qcMoudle' }"
        name="files"
        multiple
        accept=".png,.jpg,.jpeg"
        list-type="picture-card"
        :file-list="fileList"
        :disabled="disableAction || !chkitemCode"
        :before-upload="beforeUpload"
        :on-error="onUploadError"
        :on-success="onUploadSuccess"
      >
        <i slot="default" class="el-icon-plus" ref="uploadBtn" />
        <template slot="file" slot-scope="{ file }">
          <div v-if="file.isFolder" class="item is-folder" @dblclick="openFolder(file)" @contextmenu.prevent="showFolderMenu($event, file)">
            <svg-icon icon-class="folder" style="font-size: 40px" />
            <div class="folder-name ellipsis2" :title="file.fileName">{{ file.fileName }}</div>
          </div>
          <!-- <div v-else class="item" :class="{ checked: file.checked }" @click="toggleSelect(file)" @contextmenu.prevent="showFileMenu($event, file)"> -->
          <div v-else class="item" :class="{ checked: file.checked }" @click="toggleSelect(file)">
            <el-image class="el-upload-list__item-thumbnail g-img-placeholder" :src="file.url" fit="cover" />
            <span class="el-upload-list__item-actions actions">
              <span @click.stop="imgsPreview(file)">
                <i class="el-icon-zoom-in" />
              </span>
              <span @click.stop="handleRemove(file)">
                <i class="el-icon-delete" />
              </span>
            </span>
          </div>
        </template>
      </el-upload>
    </div>

    <div slot="footer" class="footer-box">
      <el-pagination
        background
        layout="total, sizes, prev, pager, next"
        :page-sizes="[20, 30, 50, 100]"
        :total="taskFileList.length"
        :page-size.sync="pageSize"
        :current-page.sync="pageNum"
        @size-change="handlePaging"
        @current-change="handlePaging"
        v-show="taskFileList.length"
      />
      <div v-show="!taskFileList.length" />
      <div>
        <span class="text-selected" v-if="selected.length"> 已选择 {{ selected.length }} 张 </span>
        <el-button type="success" :disabled="disableAction" v-if="selected.length" @click="openSelectFolder">移动到</el-button>
        <el-button type="primary" :disabled="disableAction || !selected.length" @click="btnSubmit">确 定</el-button>
        <el-button :disabled="disableAction" @click="show = false">取 消</el-button>
      </div>
    </div>

    <el-image ref="previewRef" class="hide" :src="previewSrc" :preview-src-list="previewSrcList" />
    <SelectFolder ref="SelectFolder" title="移动到文件夹" @selected="onSelectFolder" />
  </el-dialog>
</template>
<script>
// 根据检查项分类显示图片文件列表。支持多选
import { debounce } from '@/utils'
import { FILE_MULTI_UPLOAD_URL, getFileURL } from '@/api/common'
import { getTaskFileList, addRdcTaskFile, editRdcTaskFile, deleteRdcTaskFile } from '@/api/projectInspection/courseCheck'
import { listNextLevel } from '@/api/checkScore/cqEval'
import SelectFolder from './SelectFolder'
const specialChar = /^.*[¿|?|!|¡|!|!|\\].*$/
const acceptTypeList = ['image/jpeg', 'image/png', 'image/jpg']
const uploadStatusList = ['ready', 'uploading']
export default {
  name: 'SelectFileList',
  components: { SelectFolder },
  props: {
    value: { type: Boolean, default: false },
    multiple: { type: Boolean, default: false }
  },
  data() {
    return {
      FILE_MULTI_UPLOAD_URL,
      show: false,
      taskId: '',
      folderPaths: [], // 文件夹目录
      addFolderLoading: false,
      editFolderLoading: false,
      removeFolderLoading: false,
      uploading: false,
      removeLoading: false,
      listLoading: false,
      pageNum: 1,
      pageSize: 20,
      taskFileList: [],
      fileList: [],
      previewSrcList: [],
      previewSrc: '',
      chkitemType: '', // 检查项分类
      firChkList: [], // 一级检查项
      firChkLoading: false,
      chkitemCode: ''
    }
  },
  computed: {
    loadingText() {
      if (this.uploading) return '图片上传处理中'
      if (this.removeLoading) return '图片删除处理中'
      if (this.listLoading) return '加载中'
      if (this.editFolderLoading) return '重命名文件夹'
      if (this.removeFolderLoading) return '删除文件夹处理中'
      if (this.moveToFolderLoading) return '移动到文件夹'
      return ''
    },
    disableAction() {
      return !!this.loadingText || this.addFolderLoading
    },
    chkCode2name() {
      return this.firChkList.reduce((o, i) => !void (o[i.chkitemCode] = i.chkitemName) && o, {})
    },
    currFolderId() {
      const list = this.folderPaths || []
      if (!list.length) return ''

      const last = list[list.length - 1]
      return last?.taskFileId || ''
    },
    // 展示的目录
    showFolderPaths() {
      const list = this.folderPaths
      // 目录超过4级只显示最后3级
      if (list.length > 4) list.splice(1, list.length - 4, { showFileName: '...' })
      return list
    },
    showChkName() {
      const chkCode = this.chkitemCode
      return chkCode ? this.chkCode2name[chkCode] || '' : ''
    },
    selected() {
      return this.fileList.filter(i => i.taskFileId && i.checked)
    }
  },
  watch: {
    currFolderId() {
      this.getList()
    },
    uploading(val, oldVal) {
      // 批量上传结束
      if (!val && oldVal) this.getList()
    }
  },
  created() {
    this.btnSubmit = debounce(this.onSubmit, 1e3, true)
    this.initData = JSON.parse(JSON.stringify(this.$data))
  },
  methods: {
    closed() {
      this.taskId = ''
      const data = JSON.parse(JSON.stringify(this.initData))
      Object.keys(data).map(k => (this[k] = data[k]))
    },
    open(item = {}) {
      const { chkitemType, taskId, chkitemCode, firChkList = [] } = item
      if (!chkitemType || !taskId) return this.msgError('无效参数')

      this.chkitemType = chkitemType
      this.taskId = taskId
      this.chkitemCode = chkitemCode || ''
      this.show = true

      // 无传入检查项时，组件内部自动获取
      this.firChkList = firChkList
      if (!firChkList.length) this.getFirChkList()

      this.getList()
    },
    async getFirChkList() {
      this.firChkLoading = true
      const res = await listNextLevel({ chkitemCode: this.chkitemType }).catch(() => false)
      this.firChkLoading = false
      if (res === false) {
        this.chkitemCode = ''
        return (this.firChkList = [])
      }

      const data = res.data || []
      this.firChkList = data
      if (!this.chkitemCode) this.chkitemCode = data[0]?.chkitemCode
    },
    async getList() {
      const { taskId, chkitemCode } = this
      if (!taskId) return

      const opt = {
        taskId,
        fileType: chkitemCode,
        parentId: this.currFolderId
      }
      // 无检查项时，仅展示文件夹
      if (!chkitemCode) opt.isFolder = 1

      this.listLoading = true
      const res = await getTaskFileList(opt).catch(() => false)
      this.listLoading = false
      if (res === false) {
        this.taskFileList = []
        return (this.fileList = [])
      }

      const data = res.data || []
      data.map(i => {
        i.checked = false
        i.url = getFileURL(i.fileId)

        // 处理文件夹名过长
        if (!i.isFolder) return
        const name = i.fileName || ''
        i.showFileName = name.length > 20 ? `...${name.slice(-8)}` : name
      })
      // 文件夹前置
      data.sort((a, b) => b.isFolder - a.isFolder)
      this.taskFileList = data

      this.pageNum = 1
      this.handlePaging()
    },
    handlePaging() {
      // 本地分页
      const { pageNum, pageSize } = this
      const offset = Math.max(0, pageNum - 1) * pageSize
      this.taskFileList.map(i => (i.checked = false))
      this.fileList = this.taskFileList.slice(offset, offset + pageSize)
    },
    onChangeChkCode() {
      this.folderPaths = []
      this.getList()
    },
    async onSubmit() {
      const { selected } = this
      if (!selected.length) return this.msgInfo('请选择图片')

      let data = this.multiple ? selected : selected[0]
      data = JSON.parse(JSON.stringify(data))
      data.map(i => delete i.checked)

      this.$emit('ok', data)
      this.show = false
    },
    clickUpload() {
      if (!this.chkitemCode) return this.msgInfo('请选择检查项')
      this.$refs?.uploadBtn.click()
    },
    beforeUpload(file) {
      const isAccept = acceptTypeList.includes(file.type)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isAccept) this.msgError('上传图片只能是 JPG/PNG 格式!')
      if (!isLt10M) this.msgError('上传图片大小不能超过 10MB!')

      const isOk = isAccept && isLt10M
      if (!this.uploading) this.uploading = isOk
      return isOk
    },
    finishUpload(fileList) {
      // 无上传中的项时关闭 loading
      if (!fileList.some(i => uploadStatusList.includes(i.status))) this.uploading = false
    },
    onUploadError(err, file, fileList) {
      this.finishUpload(fileList)
      this.msgError(`上传失败 ${err?.msg || ''}`)
    },
    async onUploadSuccess(res, file, fileList) {
      const { taskId, chkitemCode } = this
      if (res.code !== 200) {
        this.finishUpload(fileList)
        return this.msgError(`上传失败 ${res.msg || ''}`)
      }
      // 绑定到当前记录
      const opt = {
        taskId,
        fileType: chkitemCode,
        isFolder: 0,
        parentId: this.currFolderId,
        fileId: res.data[0],
        fileName: file.name
      }
      const res2 = await addRdcTaskFile(opt).catch(() => false)
      this.finishUpload(fileList)
      if (res2 === false) return this.msgError('添加图片失败')
    },
    imgsPreview(file) {
      this.previewSrcList = this.fileList.filter(i => !i.isFolder).map(i => i.url)
      this.previewSrc = file.url
      this.$nextTick(() => this.$refs.previewRef?.clickHandler())
    },
    async handleRemove(file) {
      const res1 = await this.$confirm('确定删除此图片?', '警告', { type: 'warning' }).catch(() => false)
      if (res1 !== 'confirm') return

      const { taskFileId } = file
      this.removeLoading = true
      const res = await deleteRdcTaskFile({ taskFileId }).catch(() => false)
      this.removeLoading = false
      if (res === false) return

      // 从列表移除
      const index = this.taskFileList.findIndex(i => i.taskFileId === taskFileId)
      if (index > -1) this.taskFileList.splice(index, 1)
      // 从当前页移除
      const index2 = this.fileList.findIndex(i => i.taskFileId === taskFileId)
      if (index2 > -1) this.fileList.splice(index2, 1)
      this.msgSuccess('删除成功')

      // 回到第一页
      if (!this.fileList.length) {
        this.pageNum = 1
        this.handlePaging()
      }
    },
    toggleSelect(file) {
      const { taskFileId } = file
      if (!taskFileId) return this.msgInfo('未上传成功，不能操作')
      // if (file.status !== 'success') return this.msgInfo('未上传成功，不能操作')
      file.checked = !file.checked
      if (!this.multiple) this.fileList.map(i => i.taskFileId !== taskFileId && (i.checked = false))
    },
    async addFolder() {
      const { taskId, chkitemCode } = this
      if (!chkitemCode) return this.msgInfo('请选择检查项')

      const res1 = await this.$prompt('', '新建文件夹', {
        inputPlaceholder: '请输入文件夹名称',
        inputValidator: val => {
          const str = (val || '').trim()
          if (!str) return false
          if (specialChar.test(str)) return '文件夹名称不允许部分特殊字符'
          if (str.length > 20) return '文件夹名称最多20个字符'
          return true
        },
        inputErrorMessage: '请输入文件夹名称'
      }).catch(action => ({ action }))
      if (res1.action !== 'confirm') return

      const fileName = (res1.value || '').trim()
      const opt = {
        taskId,
        fileType: chkitemCode,
        isFolder: 1,
        parentId: this.currFolderId,
        fileName
      }
      this.addFolderLoading = true
      const res = await addRdcTaskFile(opt).catch(() => false)
      this.addFolderLoading = false
      if (res === false) return

      this.msgSuccess('新建文件夹成功')
      this.getList()
    },
    switchFolder(item, index) {
      // 返回根目录
      if (item.action === 'root') {
        if (!this.chkitemCode && !this.folderPaths.length) return
        this.chkitemCode = ''
        this.folderPaths = []
        return this.getList()
      }
      // 返回检查项
      if (item.action === 'chkCode') return (this.folderPaths = [])
      // 最后一级
      if (item.taskFileId === this.currFolderId) return
      // 移除子层级
      this.folderPaths.splice(index + 1)
    },
    openFolder(file) {
      if (file.taskFileId === this.currFolderId) return
      if (!this.chkitemCode) this.chkitemCode = file.fileType
      this.folderPaths.push(file)
    },
    showFolderMenu(event, item) {
      this.$contextmenu({
        event,
        zIndex: 1e8,
        minWidth: 100,
        items: [
          { label: '重命名', icon: 'el-icon-edit', onClick: () => this.renameFolder(item) },
          { label: '删除', icon: 'el-icon-delete', onClick: () => this.removeFolder(item) }
        ]
      })
    },
    async renameFolder(item) {
      const res1 = await this.$prompt('', '重命名文件夹', {
        inputPlaceholder: '请输入文件夹名称',
        inputValidator: val => {
          const str = (val || '').trim()
          if (!str) return false
          if (specialChar.test(str)) return '文件夹名称不允许部分特殊字符'
          if (str.length > 20) return '文件夹名称最多20个字符'
          return true
        },
        inputErrorMessage: '请输入文件夹名称'
      }).catch(action => ({ action }))
      if (res1.action !== 'confirm') return

      const fileName = (res1.value || '').trim()
      const opt = [{ ...item, fileName }]
      this.editFolderLoading = true
      const res = await editRdcTaskFile(opt).catch(() => false)
      this.editFolderLoading = false
      if (res === false) return

      this.msgSuccess('更改成功')
      this.getList()
    },
    async removeFolder(item) {
      if (!item?.taskFileId) return

      const h = this.$createElement
      const msg = h('p', null, [h('div', null, `确定删除文件夹：${item.fileName}`), h('span', { style: 'font-size: 12px; color: #909399' }, '注：需底下无子文件或文件夹')])
      const res1 = await this.$confirm(msg, { type: 'warning' }).catch(() => false)
      if (res1 !== 'confirm') return

      const { taskFileId } = item
      this.removeFolderLoading = true
      const res = await deleteRdcTaskFile({ taskFileId }).catch(() => false)
      this.removeFolderLoading = false
      if (res === false) return

      this.msgSuccess('删除成功')
      this.getList()
    },
    showFileMenu(event, item) {
      this.$contextmenu({
        event,
        zIndex: 1e8,
        minWidth: 100,
        items: [
          { label: '预览大图', icon: 'el-icon-zoom-in', onClick: () => this.previewFile(item) },
          { label: '删除', icon: 'el-icon-delete', onClick: () => this.removeFile(item) }
        ]
      })
    },
    previewFile(item) {
      this.previewSrcList = this.fileList.filter(i => !i.isFolder).map(i => i.url)
      this.previewSrc = item.url
      this.$nextTick(() => this.$refs.previewRef?.clickHandler())
    },
    async removeFile(item) {
      const res1 = await this.$confirm('确定删除此图片?', '警告', { type: 'warning' }).catch(() => false)
      if (res1 !== 'confirm') return

      const { taskFileId } = item
      this.removeLoading = true
      const res = await deleteRdcTaskFile({ taskFileId }).catch(() => false)
      this.removeLoading = false
      if (res === false) return

      this.msgSuccess('删除成功')
      this.getList()
    },
    openSelectFolder() {
      const { taskId, chkitemCode } = this
      if (!chkitemCode) return this.msgInfo('请选择检查项')

      const opt = {
        taskId,
        fileType: chkitemCode,
        rootPathName: this.showChkName
      }
      this.$refs.SelectFolder.open(opt)
    },
    async onSelectFolder({ taskFileId }) {
      if (taskFileId === this.currFolderId) return this.msgInfo('已在当前文件夹')

      const list = JSON.parse(JSON.stringify(this.selected))
      list.map(i => (i.parentId = taskFileId))

      this.moveToFolderLoading = true
      const res = await editRdcTaskFile(list).catch(() => false)
      this.moveToFolderLoading = false
      if (res === false) return

      this.msgSuccess('更改成功')
      this.getList()
    }
  }
}
</script>
<style lang="scss" scoped>
.select-file-List-dialog {
  /deep/ .el-dialog__body {
    padding: 10px 20px;
  }
}
.loading {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 99;
}
.folder-paths {
  padding: 10px;
  background: linear-gradient(90deg, #f5f7fa, rgba(#f5f7fa, 0));
  margin-bottom: 12px;
  user-select: none;
}
.upload-box {
  min-height: 300px;
  max-height: calc(88vh - 250px);
  overflow-y: auto;
  border: 1px solid #ebebeb;
  border-radius: 6px;
  padding: 15px;
  /deep/ {
    .el-upload-list--picture-card .el-upload-list__item {
      width: 100px;
      height: 100px;
      border: none;
      transition: none;
    }
    .el-upload--picture-card {
      width: 100px;
      height: 100px;
      line-height: 100px;
    }
  }
  .item {
    width: 100%;
    height: 100%;
    border-radius: 6px;
    border: 1px solid #c0ccda;
    overflow: hidden;
    cursor: pointer;
    &.checked {
      border: 3px solid #67c23a;
      .actions {
        left: 3px;
        top: 3px;
        width: calc(100% - 6px);
        height: calc(100% - 6px);
      }
    }
    &.is-folder {
      padding-top: 16px;
      text-align: center;
      border-color: transparent;
      font-size: 12px;
      user-select: none;
      &:hover,
      &:focus {
        background-color: #f5f7fa;
      }
    }
    .folder-name {
      line-height: 16px;
      margin-top: 6px;
    }
  }
  .actions {
    cursor: pointer;
  }
}
.text-selected {
  font-size: 14px;
  color: #606266;
  margin-right: 20px;
}
.footer-box {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>
